package falcon.protocol;

import java.util.ArrayList;

import falcon.protocol.impl.AckPacket;
import falcon.protocol.impl.FlowPacket;
import falcon.protocol.impl.BashPacket;
import falcon.protocol.impl.FuncPacket;
import falcon.protocol.impl.HostPacket;
import falcon.protocol.impl.SysPacket;
import sun.security.krb5.internal.HostAddress;

public final class PacketFactory {
	
	public static final int ACK = 1;
	public static final int SYS = 2;
	public static final int BASH = 3;
	public static final int FUNC = 4;
	public static final int FLOW = 5;
	public static final int HOST = 6;

	public enum FieldType {
		Short,
		Int,
		String,
	}
	
	public static final class FieldBytes {
		public static final int VAR_BYTES = 0;
		public static final int FIX_SHORT = 2;
		public static final int FIX_INT = 4;
	}
	
	public static final class FieldInfo {
		public String name;
		public FieldType type;
		public int bytes;
	}
	
	public static Packet create(int cmd, int header) {
		Packet packet;
		switch (cmd) {
		case ACK:
			packet = new AckPacket();
			break;
		case SYS:
			packet = new SysPacket();
			break;
		case BASH:
			packet = new BashPacket();
			break;
		case FUNC:
			packet = new FuncPacket();
			break;
		case FLOW:
			packet = new FlowPacket();
			break;
		case HOST:
			packet = new HostPacket();
			break;
		default:
			return null;
		}
		if (header != 0 && Packet.getCmd(header) == cmd) {
			packet.header = header;
		} else {
			packet.setVersion(Protocol.VERSION);
			packet.setCmd(cmd);
		}
		return packet;
	}
	
	public static FieldInfo[] getFields(int cmd) {
		if (cmd < 0 || cmd >= s_fieldsArray.length) {
			return null;
		}
		return s_fieldsArray[cmd];
	}
	
	private static void addField(String name, FieldType type, ArrayList<FieldInfo> list) {
		FieldInfo info = new FieldInfo();
		info.name = name;
		info.type = type;
		info.bytes = type == FieldType.Int ? FieldBytes.FIX_INT : FieldBytes.FIX_SHORT;
		list.add(info);
		if (type == FieldType.String) {
			info = new FieldInfo();
			info.name = name;
			info.type = type;
			info.bytes = FieldBytes.VAR_BYTES;
			list.add(info);
		}
	}
	
	private static FieldInfo[][] s_fieldsArray = new FieldInfo[Byte.MAX_VALUE][];
	
	static {
		ArrayList<FieldInfo> list = new ArrayList<PacketFactory.FieldInfo>();
		
		list.clear();
		addField("header", FieldType.Int, list);
		addField("id", FieldType.Int, list);
		addField("errno", FieldType.Int, list);
		addField("desc", FieldType.String, list);
		s_fieldsArray[ACK] = list.toArray(new FieldInfo[0]);
		
		list.clear();
		addField("header", FieldType.Int, list);
		addField("id", FieldType.Int, list);
		addField("action", FieldType.Int, list);
		addField("parameter", FieldType.String, list);
		s_fieldsArray[SYS] = list.toArray(new FieldInfo[0]);
		
		list.clear();
		addField("header", FieldType.Int, list);
		addField("id", FieldType.Int, list);
		addField("cmd", FieldType.String, list);
		addField("host", FieldType.String, list);
		addField("port", FieldType.Int, list);
		addField("user", FieldType.String, list);
		s_fieldsArray[BASH] = list.toArray(new FieldInfo[0]);

		list.clear();
		addField("header", FieldType.Int, list);
		addField("id", FieldType.Int, list);
		s_fieldsArray[FUNC] = list.toArray(new FieldInfo[0]);
		
		list.clear();
		addField("header", FieldType.Int, list);
		addField("id", FieldType.Int, list);
		addField("action", FieldType.Int, list);
		addField("name", FieldType.String, list);
		addField("parameter", FieldType.String, list);
		s_fieldsArray[FLOW] = list.toArray(new FieldInfo[0]);
		
		list.clear();
		addField("header", FieldType.Int, list);
		addField("id", FieldType.Int, list);
		addField("action", FieldType.Int, list);
		addField("cipher", FieldType.String, list);
		addField("parameter", FieldType.String, list);
		s_fieldsArray[HOST] = list.toArray(new FieldInfo[0]);
	}
}
